﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace OperationPlugins.Validators
{
    /// <summary>
    /// A container for validation result messages.
    /// </summary>
    public class ValidationResultMessageContainer
    {
        #region Fields

        private readonly List<ValidationResultMessageContainer> _childContainers = new List<ValidationResultMessageContainer>();
        private readonly List<ValidationResultMessage> _messages = new List<ValidationResultMessage>();

        private const string ReadOnlyMessage =
            @"This ValidationResult object is readonly. Try to create a new ValidationResult object by using the default constructor.";

        #endregion

        /// <summary>
        /// Represents the success of the validation.
        /// </summary>
        public static readonly ValidationResultMessageContainer Success = new ValidationResultMessageContainer(true);

        /// <summary>
        /// Initializes a new instance of the ValidationResultMessageContainer class.
        /// </summary>
        public ValidationResultMessageContainer()
            : this(false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ValidationResultMessageContainer class.
        /// </summary>
        /// <param name="isReadonly">True if the ValidationResultMessageContainer object should be readonly, otherwise false.</param>
        protected ValidationResultMessageContainer(bool isReadonly)
        {
            IsReadonly = isReadonly;
        }

        /// <summary>
        /// Is this ValidationResultMessageContainer object readonly!
        /// </summary>
        protected bool IsReadonly { set; get; }

        /// <summary>
        /// Gets a Boolean value indicating whether this ValidationResultMessageContainer object or one of its child 
        /// ValidationResultMessageContainer objects contains any error messages.
        /// </summary>
        public virtual bool ContainsErrors
        {
            get { return _messages.Any(message => message.IsError) || _childContainers.Any(result => result.ContainsErrors); }
        }

        /// <summary>
        /// Gets a Boolean value indicating whether this ValidationResultMessageContainer object or one of its child 
        /// ValidationResultMessageContainer objects contains any notification messages.
        /// </summary>
        public virtual bool ContainsNotifications
        {
            get
            {
                return _messages.Any(message => message.IsNotification) ||
                       _childContainers.Any(result => result.ContainsNotifications);
            }
        }

        /// <summary>
        /// Gets all error messages.
        /// </summary>
        public virtual ReadOnlyCollection<ValidationResultMessage> Errors
        {
            get
            {
                var l = new List<ValidationResultMessage>();

                l.AddRange(_messages.Where(message => message.IsError));

                foreach (ValidationResultMessageContainer container in _childContainers)
                {
                    l.AddRange(container.Errors);
                }

                return l.AsReadOnly();
            }
        }

        /// <summary>
        /// Gets all notification messages.
        /// </summary>
        public virtual ReadOnlyCollection<ValidationResultMessage> Notifications
        {
            get
            {
                var l = new List<ValidationResultMessage>();

                l.AddRange(_messages.Where(message => message.IsNotification));

                foreach (ValidationResultMessageContainer container in _childContainers)
                {
                    l.AddRange(container.Notifications);
                }

                return l.AsReadOnly();
            }
        }

        /// <summary>
        /// Adds a validation result message to this ValidationResultMessageContainer.
        /// </summary>
        /// <param name="message">The validation result message to be added.</param>
        public virtual void Add(ValidationResultMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            if (IsReadonly)
            {
                throw new InvalidOperationException(ReadOnlyMessage);
            }

            _messages.Add(message);
        }

        /// <summary>
        /// Adds a child validation result to this ValidationResultMessageContainer.
        /// </summary>
        /// <param name="result">The child validation result to be added.</param>
        public virtual void Add(ValidationResultMessageContainer result)
        {
            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            if (IsReadonly)
            {
                throw new InvalidOperationException(ReadOnlyMessage);
            }

            _childContainers.Add(result);
        }

        internal void Clear()
        {
            _childContainers.Clear();
            _messages.Clear();
        }

    }
}